/** \file    mswin/work_thread.h
  * \brief   Thread that awaits orders to do some useful work in background.
  * \date    2003 - 2004
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/mswin/work_thread.h,v 1.1 2004/05/02 18:59:40 cscom1r4 Exp $
  */

#ifndef mswin_work_thread_h
#define mswin_work_thread_h

#include "shared_resource.h"
#include "event.h"
#include "thread.h"

namespace mswin {

class WorkThreadBase : public thread, protected shared_resource
{
public:
	WorkThreadBase();

public: //to call from another thread
	///signals the Work thread to terminate
	void Terminate();
	///indicates that the thread is asleep and only awaiting new orders
	bool IsIdle() const;
	///waits while the thread completes all its orders and is asleep
	void WaitIdle() const;
	///returns whenever the thread is idle or GUI event happened
	///on idle return true otherwise false
	///\param DispatchMsg order the method to disptach GUI events before return
	bool IdleOrWndMsg(bool DispatchMsg) const;
	///same as WaitIdle() but also processes incoming GUI events (to call from GUI thread)
	void WaitIdleWithMsgProcessing() const;

public:	//to call from Work thread itself
	bool IsCanceled() const
		{ return m_Canceled; }

protected:
	///signals from another thread what Work thread is not idle any more due to new order receipt
	void WorkAppeared();
	///from Work thread awaits receipt of new order or termination
	void WaitOrder();
	///from Work thread on exit
	void IamFree();

protected:
	volatile bool m_Canceled;
	volatile bool m_Terminated;

private: //synchronization
	event m_Event;	///< wakes Work thread
	event m_IdleEvent;
};

template <class Q>
class WorkThread : public WorkThreadBase
{
public:
	//bool IsEmpty(), Clear(), bool Push(const OrderType&), Pop(OrderType&), Remove(const OrderType&) operations
	typedef Q QueueType; 
	typedef typename QueueType::OrderType OrderType;

public: //to call from GUI thread
	///appends new order to waiting ones
	void Place(const OrderType & o);
	///deletes single order from the queue or cancels its processing if it was started
	void Remove(const OrderType & o);
	///if the same order already in processing or was last processed do nothing (return false), 
	///overwise cancels everything and starts processing this new order (return true)
	bool Replace(const OrderType & o);
	///cancels all previous orders even those processing began
	void Cancel();
	///Cancel() supplemented with WaitIdle()
	void CancelWaitIdle();

protected:	//to call from Work thread itself
	///indefinitely waits for a new order, returns false if Work should terminate
	bool Wait();
	///the method is called during processing of some order and returns true if at least one more enqueued
	bool HasAnotherOrder();
	///gives access to order after successful wait
	const OrderType & OrderBeingProcessed() const
		{ return m_OrderBeingProcessed; }
	///returns true if processing of last order received with Wait() should be canceled
	virtual void Execute(const OrderType & o) = 0;

protected:
	//implementation of mswin::thread
    virtual void run();

private: //order's data
	/*volatile*/ QueueType m_Queue;
	OrderType m_OrderBeingProcessed; //or was last processed and not the thread is idle
};

///////////////////////////////////////
// template functions implementation //
///////////////////////////////////////

inline WorkThreadBase::WorkThreadBase() :
	m_Terminated(false), m_Event(true), m_IdleEvent(false) //assume that initially thread is idle
{
	m_Event.set();
	restart_on_error(true);
}

inline void WorkThreadBase::Terminate()
{
	auto_lock lock(*this);
	m_Terminated = true;
	m_Event.set();
}

inline bool WorkThreadBase::IsIdle() const
{
	return m_IdleEvent.is_set(); 
}

inline void WorkThreadBase::WaitIdle() const
{
	m_IdleEvent.wait(); 
}

inline bool WorkThreadBase::IdleOrWndMsg(bool DispatchMsg) const
{
	MsgWaitForMultipleObjects(1, 
		const_cast<HANDLE*>(&m_IdleEvent.h), 
		FALSE, INFINITE, QS_ALLINPUT);
	if (DispatchMsg) {
		MSG msg;
		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			TranslateMessage(&msg); 
			DispatchMessage(&msg); 
		}
	}
	return IsIdle();
}

inline void WorkThreadBase::WaitIdleWithMsgProcessing() const
{
	while (!IdleOrWndMsg(true));
}

inline void WorkThreadBase::WorkAppeared()
{
	//do not change the following order of events: reset, then set
	m_IdleEvent.reset();
	m_Event.set();
}

inline void WorkThreadBase::WaitOrder()
{
	//do not set idle event if there are uncompleted orders
	if (!m_Event.is_set()) {
		m_IdleEvent.set();
		m_Event.wait();
		m_IdleEvent.reset();
	}
}

inline void WorkThreadBase::IamFree()
{
	m_IdleEvent.set();
}

template <class Q>
inline void WorkThread<Q>::Place(const OrderType & o)
{
	auto_lock lock(*this);
	if (!m_Queue.Push(o)) //Push() returns false if this order abolish previous ones
		m_Canceled = true;
	WorkAppeared();
}

template <class Q>
inline void WorkThread<Q>::Remove(const OrderType & o)
{
	auto_lock lock(*this);
	m_Queue.Remove(o);
	if (m_OrderBeingProcessed == o)
		m_Canceled = true;
}

template <class Q>
bool WorkThread<Q>::Replace(const OrderType & o)
{
	auto_lock lock(*this);
	if (m_OrderBeingProcessed == o && m_Queue.IsEmpty())
		return false;
	m_Canceled = true;
	m_Queue.Clear();
	m_Queue.Push(o);
	WorkAppeared();
	return true;
}

template <class Q>
inline void WorkThread<Q>::Cancel()
{
	auto_lock lock(*this);
	m_Canceled = true;
	m_Queue.Clear();
}

template <class Q>
inline void WorkThread<Q>::CancelWaitIdle()
{
	Cancel();
	WaitIdle();
}

template <class Q>
bool WorkThread<Q>::Wait()
{
	for (;;) {
		auto_lock lock(*this);

		if (m_Queue.IsEmpty()) {
			lock.unlock();
			WaitOrder();

			if (m_Terminated)
				return false;

			lock.lock();
			if (m_Queue.IsEmpty())
				continue; //it may happen that an order was canceled simultaneously with the return from WaitFor...()
		}

		m_Queue.Pop(m_OrderBeingProcessed);
		m_Canceled = false;
		return true;
	}
}

template <class Q>
inline bool WorkThread<Q>::HasAnotherOrder()
{
	auto_lock lock(*this);
	return !m_Queue.IsEmpty();
}

template <class Q>
void WorkThread<Q>::run()
{
	while (Wait()) {
		Execute(OrderBeingProcessed());
	}
	IamFree();
}

} //namespace mswin

#endif //mswin_work_thread_h
